using System;
using System.IO;
using Microsoft.Win32;

namespace AbstractClass.IO
{
    /// <summary>
    /// Provides a set of static methods for registering file associations.
    /// </summary>
    /// <remarks>CREDIT: The code has been taken from Sharpdevelop's FiletypeRegisterer addin.</remarks>
    public static class RegisterFiletypesCommand
    {
        /// <summary>
        /// Registers a file extension.
        /// </summary>
        /// <param name="type">The type association details.</param>
        /// <param name="exeFileLocation">The executable file location.</param>
        public static void RegisterFileType(FiletypeAssociation type, string exeFileLocation)
        {
            string mainExe = exeFileLocation;
            RegisterFileType(type.Extension, type.ProgId,
                             type.Text,
                             '"' + Path.GetFullPath(mainExe) + '"' + " \"%1\"",
                             Path.GetFullPath(type.Icon));
        }

        /// <summary>
        /// Determines whether the specified extension is registered.
        /// </summary>
        /// <param name="extension">The extension.</param>
        /// <param name="exeFileLocation">The executable file location.</param>
        /// <returns>
        /// 	<c>true</c> if the specified extension is registered; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsRegistered(string extension, string exeFileLocation)
        {
            string openCommand = GetOpenCommand(extension);

            if (string.IsNullOrEmpty(openCommand))
                return false;

            string mainExe = exeFileLocation;
            return openCommand.StartsWith(mainExe) || openCommand.StartsWith('"' + mainExe);
        }

        const string ExplorerFileExts = @"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts";

        /// <summary>
        /// Determines whether the specified extension is registered.
        /// </summary>
        /// <param name="extension">The extension.</param>
        /// <returns>
        /// 	<c>true</c> if the specified extension is registered; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsRegisteredFileType(string extension)
        {
            try
            {
                using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("." + extension))
                {
                    if (key != null)
                        return true;
                }
            }
            catch (System.Security.SecurityException)
            {
                // registry access might be denied
            }
            try
            {
                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(ExplorerFileExts + "\\." + extension))
                {
                    if (key != null)
                        return true;
                }
            }
            catch (System.Security.SecurityException)
            {
                // registry access might be denied
            }
            return false;
        }

        static string GetOpenCommand(string extension)
        {
            try
            {
                string clsKeyName = null;
                using (RegistryKey extKey = Registry.CurrentUser.OpenSubKey(ExplorerFileExts + "\\." + extension))
                {
                    if (extKey != null)
                        clsKeyName = (string)extKey.GetValue("Progid", "");
                }
                if (string.IsNullOrEmpty(clsKeyName))
                {
                    using (RegistryKey extKey = Registry.ClassesRoot.OpenSubKey("." + extension))
                    {
                        if (extKey != null)
                            clsKeyName = (string)extKey.GetValue("", "");
                        else
                            return null;
                    }
                }
                using (RegistryKey cmdKey = Registry.ClassesRoot.OpenSubKey(clsKeyName + "\\shell\\open\\command"))
                {
                    return cmdKey != null ? (string) cmdKey.GetValue("", "") : null;
                }
            }
            catch (System.Security.SecurityException)
            {
                // registry access might be denied
                return null;
            }
        }

        /// <summary>
        /// Registers the file extension.
        /// </summary>
        /// <param name="extension">The extension.</param>
        /// <param name="progId">The unique program id.</param>
        /// <param name="description">The description of the file.</param>
        /// <param name="command">The associated shell command.</param>
        /// <param name="icon">The default icon of the file.</param>
        public static void RegisterFileType(string extension, string progId, string description, string command, string icon)
        {
            try
            {
                RegisterFileType(Registry.ClassesRoot, progId, extension, description, command, icon);
            }
            catch (UnauthorizedAccessException)
            {
                try
                {
                    RegisterFileType(Registry.CurrentUser.CreateSubKey("Software\\Classes"), progId, extension, description, command, icon);
                }
// ReSharper disable EmptyGeneralCatchClause
                catch { }
// ReSharper restore EmptyGeneralCatchClause
            }
            NotifyShellAfterChanges();
        }

        static void RegisterFileType(RegistryKey rootKey, string progId, string extension, string description, string command, string icon)
        {
            RegistryKey extKey = rootKey.CreateSubKey("." + extension);
            // save previous association
            if (extKey != null)
            {
                var prev = (string)extKey.GetValue("", "");
                if (prev != "" && prev != (progId + "." + extension + "file"))
                {
                    extKey.SetValue("Pre" + progId, extKey.GetValue(""));
                }

                extKey.SetValue("", progId + "." + extension + "file");
                extKey.Close();

                try
                {
                    extKey = Registry.CurrentUser.OpenSubKey(ExplorerFileExts + "\\." + extension, true);
                    if (extKey != null)
                    {
                        extKey.DeleteValue("Progid");
                        extKey.Close();
                    }
                }
// ReSharper disable EmptyGeneralCatchClause
                catch { }
// ReSharper restore EmptyGeneralCatchClause
            }
            
            RegistryKey clsKey = rootKey.CreateSubKey(progId + "." + extension + "file");

            if (clsKey != null)
            {
                clsKey.SetValue("", description);
                var subkey = clsKey.CreateSubKey("DefaultIcon");
                if (subkey != null) subkey.SetValue("", '"' + icon + '"');
                RegistryKey openKey = clsKey.CreateSubKey("shell\\open\\command");
                if (openKey != null)
                {
                    openKey.SetValue("", command);
                    openKey.Close();
                }
                clsKey.Close();
            }
        }

        /// <summary>
        /// Unregisters the file extension.
        /// </summary>
        /// <param name="extension">The file extension.</param>
        /// <param name="progId">The unique program id.</param>
        public static void UnRegisterFiletype(string extension, string progId)
        {
            UnRegisterFiletype(extension, Registry.ClassesRoot, progId);
            try
            {
                UnRegisterFiletype(extension, Registry.CurrentUser.CreateSubKey("Software\\Classes"), progId);
            }
// ReSharper disable EmptyGeneralCatchClause
            catch { } // catch CreateSubKey(Software\Classes)-exceptions
// ReSharper restore EmptyGeneralCatchClause
            NotifyShellAfterChanges();
        }

        static void UnRegisterFiletype(string extension, RegistryKey root, string progId)
        {
            try
            {
                root.DeleteSubKeyTree(progId + "." + extension + "file");
            }
// ReSharper disable EmptyGeneralCatchClause
            catch { }
// ReSharper restore EmptyGeneralCatchClause
            try
            {
                RegistryKey extKey = root.OpenSubKey("." + extension, true);

                // if no association return
                if (extKey == null) return;
                // if other association return too
                if ((string)extKey.GetValue("", "") != (progId + "." + extension + "file")) return;

                // restore previous association
                var prev = (string)extKey.GetValue("Pre" + progId, "");
                if (prev != "")
                {
                    extKey.SetValue("", prev);
                }
                extKey.Close();
                if (prev != null)
                {
                    root.DeleteSubKeyTree("." + extension);
                }
            }
// ReSharper disable EmptyGeneralCatchClause
            catch { }
// ReSharper restore EmptyGeneralCatchClause
        }

        [System.Runtime.InteropServices.DllImport("shell32.dll")]
        static extern void SHChangeNotify(int wEventId, int uFlags, IntPtr dwItem1, IntPtr dwItem2);

        /// <summary>
        /// Notify Windows explorer that shortcut icons have changed.
        /// </summary>
        static void NotifyShellAfterChanges()
        {
            const int shcneAssocchanged = 0x08000000;
            const int shcnfIdlist = 0x0;

            SHChangeNotify(shcneAssocchanged, shcnfIdlist, IntPtr.Zero, IntPtr.Zero);
        }
    }
}